﻿using System;
using Unity.Burst;
using Unity.Collections;

namespace Game.FlowField.Burst
{
    [BurstCompile]
    public struct FlexibleArray<T> :IDisposable where T : struct
    {
        private int m_Capacity;
        private int m_Length;
        /// <summary>
        /// 是否填充默认值，排序的话可能需要
        /// </summary>
        private bool m_IsFillInvalidValue;
        private T m_InvalidValue;
        private Allocator m_Allocator;
        private NativeArray<T> m_Array;
        
        public int Capacity => this.m_Capacity;
        public int Length => this.m_Length;
        public NativeArray<T> Array => this.m_Array;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="capacity"></param>
        /// <param name="isFillInvalidValue">是否填充无效位置的数据</param>
        /// <param name="invalidValue">无效位置的数据</param>
        public FlexibleArray(int capacity = 4, Allocator allocator = Allocator.Temp, bool isFillInvalidValue = default, T invalidValue = default)
        {
            this.m_Allocator = allocator;
            this.m_Capacity = capacity;
            this.m_Length = 0;
            this.m_InvalidValue = invalidValue;
            this.m_IsFillInvalidValue = isFillInvalidValue;
            this.m_Array = new NativeArray<T>(capacity, this.m_Allocator);
            if (this.m_IsFillInvalidValue)
            {
                for (int i = 0; i < this.m_Array.Length; i++)
                {
                    this.m_Array[i] = invalidValue;
                }
            }
        }

        [BurstCompile]
        public void Add(T item)
        {
            if (this.m_Length == this.m_Capacity)
            {
                this.CheckCapacity(this.m_Length + 1);
            }

            this.m_Array[this.m_Length] = item;
            ++this.m_Length;
        }

        [BurstCompile]
        public void Remove(T item)
        {
            var index = this.FindIndex(item);
            if (index >= 0)
            {
                for (int i = index; i < this.m_Length - 1; i++)
                {
                    this.m_Array[i] = this.m_Array[i + 1];
                }

                if (this.m_IsFillInvalidValue)
                    this.m_Array[this.m_Length - 1] = this.m_InvalidValue;

                --this.m_Length;
            }
        }

        [BurstCompile]
        private void CheckCapacity(int length)
        {
            if (length > this.m_Capacity)
            {
                this.m_Capacity *= 2;
                var newArray = new NativeArray<T>(this.m_Capacity, this.m_Allocator);

                NativeArray<T>.Copy(this.m_Array, 0, newArray, 0, this.m_Length);

                if (this.m_IsFillInvalidValue)
                {
                    for (int i = this.m_Length; i < newArray.Length; i++)
                    {
                        newArray[i] = this.m_InvalidValue;
                    }
                }

                this.m_Array.Dispose();
                this.m_Array = newArray;
            }
        }

        [BurstCompile]
        public int FindIndex(T item)
        {
            for (int i = 0; i < this.m_Length; i++)
            {
                if (this.m_Array[i].Equals(item))
                {
                    return i;
                }
            }

            return -1;
        }

        [BurstCompile]
        public void ForEach(Action<T> action)
        {
            for (int i = 0; i < this.m_Length; i++)
            {
                action.Invoke(this.m_Array[i]);
            }
        }

        [BurstCompile]
        public void Dispose()
        {
            if (this.m_Array.IsCreated)
            {
                this.m_Array.Dispose();
                this.m_Array = default;
            }
        }

        [BurstCompile]
        public void Clear()
        {
            this.m_Length = 0;
            if (this.m_IsFillInvalidValue)
            {
                for (int i = 0; i < this.m_Array.Length; i++)
                {
                    this.m_Array[i] = this.m_InvalidValue;
                }
            }
        }
    }
}